package com.zhupanlin.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

/**
 * @author zhupanlin
 * @version 1.0
 * @description: TODO
 * @date 2024/12/12 23:27
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final MyUserDetailService userDetailService;

    @Autowired
    public WebSecurityConfig(MyUserDetailService userDetailService) {
        this.userDetailService = userDetailService;
    }


   /* @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(User.withUsername("aaa").password("{noop}123").roles("admin").build());
        return userDetailsManager;
    }*/

    // springboot 对 security 默认配置中 在工厂中默认创建 AuthenticationManager
    /*@Autowired
    private void initialize(AuthenticationManagerBuilder builder) throws Exception {
        System.out.println("springboot 默认配置：" + builder);
   }*/

    // 自定义 AuthenticationManager 推荐 不会在工厂中暴露出来
    // 会覆盖原先的 UserDetailService
    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        System.out.println("自定义的AuthenticationManager：" + builder);
        builder.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
    }

    /**
     * 作用：用来将自定义 AuthenticationManager 在工厂中暴露出来，可以在任何位置注入
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 放行资源写在任何前面
        http.authorizeHttpRequests()
                .mvcMatchers("/login.html").permitAll()
                .mvcMatchers("/index").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")  // 指定默认的登录页面 一旦自定义登录页面以后必须指定登录的 url
                .loginProcessingUrl("/doLogin")
                //.successForwardUrl("/hello") // 认证成功 forward 跳转路径 始终跳转该请求
                //.defaultSuccessUrl("/index", false) // 认证成功 redirect 跳转 根据上一次保存的请求进行跳转 没有就跳该 请求
                .successHandler(new MyAuthenticationSuccessHandler()) // 认证成功时的处理 前后端分离的处理方案
                //.failureForwardUrl("/login.html") // 认证失败之后 forward 跳转
                //.failureUrl("/login.html") // 默认 认证失败之后 redirect 跳转
                .failureHandler(new MyAuthenticationFailureHandler()) // 用于自定义认证失败时的处理
                .and()
                .logout()
                //.logoutUrl("/logout") // 指定注销登录 url 默认请求方式必须：GET
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/aa", "GET"),
                        new AntPathRequestMatcher("/bb", "POST")
                ))
                .invalidateHttpSession(true) // 默认 会话失效
                .clearAuthentication(true) // 默认 清除认证标记
                .logoutSuccessHandler(new MyLogoutSuccessHandler())
                .logoutSuccessUrl("/login.html") // 注销登录 成功之后跳转页面
                .and()
                .csrf().disable(); // 禁止 csrf 跨站请求保护
    }
}
